using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using PSLibrary = Microsoft.Office.Project.Server.Library;

namespace MicrosoftFrance.MCS.EPM2007
{
    class PSIStatusing
    {
        public static void ImportTimeSheet(Guid periodUid, bool detailLog, AppLog appLog)
        {
            if (detailLog == true)
            {
                StatusingWS.ImportTimesheetDataSet importTimeSheetDs = Program.statusingDER.ReadImportTimesheetData(periodUid);
                GeneralHelper.WriteTablesFormated("New data from timesheets:", importTimeSheetDs.Tables, appLog);
            }
            Program.statusingDER.ImportTimesheet(periodUid);
        }

        public static void UpdateStatus(TimeSheetWS.TimesheetDataSet timesheetDs, bool detailLog, AppLog appLog)
        {
            StringBuilder changeXml = CreateChangeXml(timesheetDs);

            appLog.WriteLine(String.Format("Change XML Submitted: \r\n{0}", changeXml.ToString()),false);

            Program.statusingDER.UpdateStatus(changeXml.ToString());
        }

        public static StringBuilder CreateChangeXml(TimeSheetWS.TimesheetDataSet timesheetDs)
        {
            StringBuilder changeXml = new StringBuilder();
            string pidPctWorkComplete = PSLibrary.AssnConstID.s_apid_pct_wrk_complete.ToString(System.Globalization.CultureInfo.InvariantCulture);
            string pidOvertimeWork = PSLibrary.AssnConstID.s_apid_ovt_work.ToString(System.Globalization.CultureInfo.InvariantCulture);
            string pidRegularWork = PSLibrary.AssnConstID.s_apid_regular_work.ToString(System.Globalization.CultureInfo.InvariantCulture);

            changeXml.AppendFormat("<Changes>");
            for (int i = 0; i < timesheetDs.Lines.Count; i++)
            {
                if (timesheetDs.Lines[i].TS_LINE_VALIDATION_TYPE == (int)PSLibrary.TimesheetEnum.ValidationType.Verified)
                {
                    changeXml.AppendFormat("<Proj ID=\"{0}\">", timesheetDs.Lines[i].PROJ_UID.ToString());
                    changeXml.AppendFormat("<Assn ID=\"{0}\">", timesheetDs.Lines[i].ASSN_UID.ToString());
                    //changeXml.AppendFormat("<Change PID=\"{0}\">{1}</Change>", pidPctWorkComplete, "50");
                    //changeXml.AppendFormat("<Change PID=\"{0}\">{1}</Change>", pidOvertimeWork, 60 * 1000 * 8);
                    changeXml.AppendFormat("<Change PID=\"{0}\">{1}</Change>", pidRegularWork, timesheetDs.Lines[i].TS_LINE_ACT_SUM_VALUE.ToString(System.Globalization.CultureInfo.InvariantCulture));
                    changeXml.Append("</Assn></Proj>");
                }
            }
            changeXml.Append("</Changes>");

            return changeXml;
        }

        public static void SubmitStatus(TimeSheetWS.TimesheetDataSet timesheetDs, bool detailLog, AppLog appLog)
        {
            List<Guid> assignments = new List<Guid>();
            StatusingWS.StatusingDataSet statDS = new StatusingWS.StatusingDataSet();

            for (int i = 0; i < timesheetDs.Lines.Count; i++)
            {
                if (timesheetDs.Lines[i].TS_LINE_VALIDATION_TYPE == (int)PSLibrary.TimesheetEnum.ValidationType.Verified)
                {
                    assignments.Add(timesheetDs.Lines[i].ASSN_UID);

                    if (detailLog)
                    {
                        statDS = Program.statusingDER.ReadStatus(timesheetDs.Lines[i].ASSN_UID, DateTime.MinValue, DateTime.MaxValue);
                        appLog.WriteLine(string.Format("SubmitStatus for Assignment for {0} ActWork {1}", statDS.Assignments[0].ASSN_UID, statDS.Assignments[0].ASSN_ACT_WORK), false);
                    }
                }
            }

            Program.statusingDER.SubmitStatus(assignments.ToArray(), "Submitted Status via PSI");
        }

        public static int UpdateAndApplyStatusApprovals(StatusingDerived statusingDER, ResourceWS.ResourceDataSet resDS, Guid siteID, bool detailLog, AppLog appLog)
        {
            int statusApproved = 0;
            // Loop over all resources that need to approve status update
            for (int r = 0; r < resDS.Resources.Count; r++)
            {
                if (!resDS.Resources[r].IsWRES_ACCOUNTNull())
                {
                    // Setup impersonation
                    StatusingDerived.SetImpersonationContext(resDS.Resources[r].RES_IS_WINDOWS_USER, resDS.Resources[r].WRES_ACCOUNT, resDS.Resources[r].RES_UID, Guid.NewGuid(), siteID, "1033");

                    // Get assignments waiting for approval
                    StatusingWS.StatusApprovalDataSet statusApprovalDs = Program.statusingDER.ReadStatusApprovalsSubmitted(false);

                    if (statusApprovalDs.StatusApprovals.Count > 0)
                        statusApproved = statusApproved + UpdateAndApplyStatusApprovals(statusApprovalDs, resDS.Resources[r].RES_UID, detailLog, appLog);
                }
            }

            return statusApproved;
        }

        public static int UpdateAndApplyStatusApprovals(StatusingWS.StatusApprovalDataSet statusApprovalDs, Guid userID, bool detailLog, AppLog appLog)
        {
            int status = statusApprovalDs.StatusApprovals.Count;
            List<Guid> projectList = new List<Guid>();
            for (int i = 0; i < status; i++)
            {
                appLog.WriteLine(string.Format("Approving assignment update for {2} to {0} in {1}", statusApprovalDs.StatusApprovals[i].TASK_NAME, statusApprovalDs.StatusApprovals[i].PROJ_NAME, statusApprovalDs.StatusApprovals[i].RES_NAME), false);
                statusApprovalDs.StatusApprovals[i].ASSN_TRANS_ACTION_ENUM = (int)PSLibrary.TaskManagement.StatusApprovalType.Accepted;

                if(!projectList.Contains(statusApprovalDs.StatusApprovals[i].PROJ_UID))
                    projectList.Add(statusApprovalDs.StatusApprovals[i].PROJ_UID);
            }

            appLog.WriteLine("Saving status updates...", false);
            Program.statusingDER.UpdateStatusApprovals(statusApprovalDs);

            appLog.WriteLine(string.Format("Applying {0} status updates...", status), false);
            Guid jobUid = Guid.NewGuid();
            Program.statusingDER.QueueApplyStatusApprovals(jobUid, "Approving all status updates via PSI utility");

            // Publish all projects that got updated
            PSIProject.PublishProjectList(projectList);

            return status;
        }

        public static StatusingWS.StatusingAssignmentsDataSet ReadAssignments(Guid[] assgnID)
        {
           return Program.statusing.ReadAssignments(assgnID);
        }

        public static StatusingWS.StatusingAssignmentsDataSet ReadAssignmentsImpersonated(Guid[] assgnID, Guid resUID, Guid siteID, string stWSUrl, StreamWriter sw)
        {
            ResourceWS.ResourceDataSet resDS = PSIResource.GetResourceDataSet(resUID);
            ExceptionHandlers exH = new ExceptionHandlers(sw);
            StatusingWS.StatusingAssignmentsDataSet statDS = new StatusingWS.StatusingAssignmentsDataSet();

            StatusingDerived statusingDerived = new StatusingDerived();
            statusingDerived.Url = stWSUrl;
            statusingDerived.Credentials = CredentialCache.DefaultCredentials;
            StatusingDerived.SetImpersonationContext(resDS.Resources[0].RES_IS_WINDOWS_USER, resDS.Resources[0].WRES_ACCOUNT, resUID, Guid.NewGuid(), siteID, "1033");

            try
            {
                return statusingDerived.ReadAssignments((Guid[])assgnID);
            }
            catch(Exception ex)
            {
                exH.HandleException(ex);
            }
            return statDS;
        }

        public static bool ContainsStatusUpdate(TimeSheetWS.TimesheetDataSet timesheetDs)
        {
            for (int i = 0; i < timesheetDs.Lines.Count; i++)
            {
                if (timesheetDs.Lines[i].TS_LINE_VALIDATION_TYPE == (int)PSLibrary.TimesheetEnum.ValidationType.Verified)
                {
                    return true;
                }
            }
            return false;
        }
    }

    class StatusingDerived : StatusingWS.Statusing
    {
        private static String ContextString = String.Empty;

        protected override WebRequest GetWebRequest(Uri uri)
        {
            //here we are overriding the GetWebRequest method and adding the 2 web request headers
            WebRequest webRequest = base.GetWebRequest(uri);
            if (ContextString != String.Empty)
            {
                webRequest.UseDefaultCredentials = true;
                webRequest.Credentials = CredentialCache.DefaultNetworkCredentials;
                webRequest.Headers.Add("PjAuth", ContextString);
                webRequest.Headers.Add("ForwardFrom", "/_vti_bin/psi/pwa.asmx");
                webRequest.PreAuthenticate = true;
                webRequest.ImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
            }
            return webRequest;
        }

        public static void SetImpersonationContext(bool isWindowsUser, String userNTAccount, Guid userGuid, Guid trackingGuid, Guid siteId, String lcid)
        {
            ContextString = GetImpersonationContext(isWindowsUser, userNTAccount, userGuid, trackingGuid, siteId, lcid);
        }

        private static String GetImpersonationContext(bool isWindowsUser, String userNTAccount, Guid userGuid, Guid trackingGuid, Guid siteId, String lcid)
        {
            PSLibrary.PSContextInfo contextInfo = new PSLibrary.PSContextInfo(isWindowsUser, userNTAccount, userGuid, trackingGuid, siteId, lcid);
            String contextString = PSLibrary.PSContextInfo.SerializeToString(contextInfo);
            return contextString;
        }
    }
}
